package top.lingkang.finalsession.javax;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.lingkang.finalsession.FinalSessionConfig;
import top.lingkang.finalsession.core.FinalGenerateSession;
import top.lingkang.finalsession.core.FinalRepository;
import top.lingkang.finalsession.core.FinalSessionHandler;
import top.lingkang.finalsession.error.FinalBaseException;
import top.lingkang.finalsession.error.FinalValidException;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * javax 适配，配置后final-session才会生效
 * 配置时请小心区分
 * {@link top.lingkang.finalsession.jakarta.FinalSessionFilter}
 * 与
 * {@link FinalSessionFilter} 的包差别
 * <pre>
 * {@code
 * @Configuration
 * @Order(-1995)
 * public class WebSessionConfig extends FinalSessionConfigurerAdapter {
 *     @Override
 *     protected void configurer(FinalSessionConfig config) {
 *         // 对项目进行配置
 *         // 设置cookie的名称，默认session id存储在cookie中
 *         config.setSessionName("token");
 *         // 自定义session存活时长 单位：毫秒
 *         config.setMaxValidTime(19951995L);
 *         // 配置id生成规则
 *         config.setIdGenerate(new IdGenerate<HttpServletRequest>() {
 *             @Override
 *             public String generateId(HttpServletRequest request, FinalSessionConfig config) {
 *                 // 自定义id的值，可以根据不同id前缀访问不同redis集群，从而实现集群无限扩展
 *                 return UUID.randomUUID().toString();
 *             }
 *         });
 *     }
 * }
 * }
 * </pre>
 *
 * @author lingkang
 * Created by 2024/5/10
 */
public class FinalSessionFilter implements Filter {
    private static boolean isLoad = false;
    private static final Logger log = LoggerFactory.getLogger(FinalSessionFilter.class);
    private FinalSessionConfig config = new FinalSessionConfig();
    // 会话存储仓库
    private FinalRepository<FinalSession> repository;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;

        long current = System.currentTimeMillis();
        // 获取sessionId
        FinalSessionHandler<HttpServletRequest, ServletResponse> handler = config.getSessionHandler();
        String id = handler.getSessionId(request, config.getSessionName());
        FinalSession session = null;
        if (id != null) {
            session = repository.getSession(id);
        }
        FinalGenerateSession<HttpServletRequest, FinalSession> generateSession = config.getGenerateSession();
        if (session == null) { // 生成会话
            session = generateSession.generateSession(request, config);
        } else {
            // 判断预留时间
            if (config.isUpdateAccessTime()) {
                // 判断令牌是否有效
                if (session.getLastAccessedTime() + config.getMaxValidTime() < current) {
                    repository.deleteSession(id);
                    // 说明之前的会话已经到期， 生成会话
                    session = generateSession.generateSession(request, config);
                } else {// 更新访问时间
                    session.updateAccessTime(current);
                }
            } else {
                if (session.getLastAccessedTime() + config.getMaxValidTime() - config.getReserveTime() < current) {
                    repository.deleteSession(id);
                    // 说明之前的会话已经到期， 重新生成会话
                    session = generateSession.generateSession(request, config);
                }
            }
        }

        // 对响应添加 cookie或自定义操作
        handler.setSessionId(servletResponse, config.getSessionName(), session.getId());

        FinalServletRequestWrapper wrapper = new FinalServletRequestWrapper(request, session);

        // 放行
        filterChain.doFilter(wrapper, servletResponse);

        // 是否更新会话最后访问时间
        if (session.isExistsUpdate() || config.isUpdateAccessTime()) {
            session.setExistsUpdate(false);
            repository.setSession(session.getId(), session);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        if (isLoad)
            throw new FinalBaseException("已经加载过 final-session 配置了，请不要再重复配置: " + getClass().getName());
        configurer(config);

        if (!config.isUpdateAccessTime()) {
            if (config.getMaxValidTime() < config.getReserveTime()) {
                log.error("最大令牌时效时间不能比预留时间小， maxValidTime < reserveTime error !",
                        new FinalValidException("最大令牌时效时间不能比预留时间小， maxValidTime < reserveTime error !"));
                System.exit(0);
            }
        }

        if (config.getGenerateSession() == null)
            config.setGenerateSession(new GenerateSessionJavax());
        if (config.getSessionHandler() == null)
            config.setSessionHandler(new FinalSessionHandlerCookie());
        if (config.getIdGenerate() == null)
            config.setIdGenerate(new FinalIdGenerate());

        repository = config.getRepository();
        if (repository == null) {
            repository = new MemoryFinalRepository();
            log.info("final-session use memory repository! final-session 使用内存作为存储，不适用于分布式会话！");
        }
        repository.initConfig(config);

        log.info("final-session init finish...");
        isLoad = true;
    }

    @Override
    public void destroy() {
        repository.destroy();
    }

    /**
     * 初始化前的配置
     *
     * @param config 配置
     */
    protected void configurer(FinalSessionConfig config) {
        this.config = config;
    }
}
